﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GnufBase.h"
#include "GnufPreferenceEntries.h"

class QWidget;

namespace {
    class CustomizedPreferenceEntryPlaceholder;
}

namespace gnuf {
    class QtPreferenceEntry;

    /// \brief CustomizedPreferenceEntry is the basic class 
    /// of any CustomizedPreferenceEntry.
    class GNUF_BASE_EXPORT CustomizedPreferenceEntry : public PreferenceEntry
    {
        DEFINE_CAST_DERIVED(CustomizedPreferenceEntry, PreferenceEntry)
    protected:
        explicit CustomizedPreferenceEntry(PreferenceEntryGroup *);
    public:
        static const PreferenceEntry::EType Type;
        virtual ~CustomizedPreferenceEntry();

        /// \return Return true if this instance is a placeholder. Return false, otherwise.
        bool isPlaceholder() const { return m_isPlaceholder; }
        
        typedef std::map<std::wstring, std::wstring> XmlAttributes;
        /// \brief Set the xml attributes.
        virtual void set(const CustomizedPreferenceEntry::XmlAttributes &) = 0;
        /// \brief Get the xml attributes.
        virtual void get(CustomizedPreferenceEntry::XmlAttributes &) const = 0;

        /// \brief Make a Qt widget and return it.
        virtual QtPreferenceEntry * makeQtWidget() = 0;

    private:
        friend class CustomizedPreferenceEntryPlaceholder;
        bool m_isPlaceholder;
    };
    
    /// \brief CustomizedPreferenceEntryCreatorRegisterer intends to register 
    /// the creator in the constructor, and unregister it in the destructor.
    class GNUF_BASE_EXPORT CustomizedPreferenceEntryCreatorRegisterer final
    {
    public:
        /// \brief Constructor.
        /// \param key: A creator must have a global only key.
        /// \param creator: The CustomizedPreferenceEntryCreator.
        explicit CustomizedPreferenceEntryCreatorRegisterer(
            const std::wstring & key,
            const CustomizedPreferenceEntryCreatorFunc & creator);
        ~CustomizedPreferenceEntryCreatorRegisterer();
    private:
        const std::wstring m_key;
    };

    /// \brief CustomizedPreferenceEntryFactory class intends to manage
    /// all the CustomizedPreferenceEntryCreator(s).
    class GNUF_BASE_EXPORT CustomizedPreferenceEntryFactory final
    {   
    private:
        CustomizedPreferenceEntryFactory();
        ~CustomizedPreferenceEntryFactory();

    public:
        /// \brief Get the global only instance.
        /// \return Return the global only instance.
        static CustomizedPreferenceEntryFactory * get();
        
        /// \brief Get the placeholder creator.
        const CustomizedPreferenceEntryCreatorFunc & placeholderCreator() const;
        
        /// \brief Add a CustomizedPreferenceEntryCreator.
        /// \param key: The key of the CustomizedPreferenceEntryCreator.
        /// \param creator: The CustomizedPreferenceEntryCreator.
        /// \return Return true if successful. Otherwise, return false.
        bool addCreator(
            const std::wstring & key,
            const CustomizedPreferenceEntryCreatorFunc & creator);
        
        /// \brief Remove the CustomizedPreferenceEntryCreator.
        /// \param key: The key of the CustomizedPreferenceEntryCreator that 
        /// wants to be removed.
        /// \return Return true if successful. Otherwise, return false.
        bool removeCreator(const std::wstring & key);
        
        /// \brief Get the CustomizedPreferenceEntryCreator through the key.
        /// \param key: The key of the CustomizedPreferenceEntryCreator.
        /// \param pCallable: If the returned CustomizedPreferenceEntryCreator
        /// is callable, pCallable is true. Otherwise, pCallable is false.
        const CustomizedPreferenceEntryCreatorFunc & creatorBy(
            const std::wstring & key, bool * pCallable = nullptr);
    
    private:
        static CustomizedPreferenceEntryFactory s_theOneAndOnly;
        typedef std::map<const std::wstring, CustomizedPreferenceEntryCreatorFunc> MapKeyToCreators;
        CustomizedPreferenceEntryFactory::MapKeyToCreators m_creators;
    };
    
    template <class TValue> class CustomizedPreferenceEntryWithValue;
    
    /// \brief CustomizedPreferenceEntryWithValueSnapshot template class intends
    /// to make a snapshot for CustomizedPreferenceEntryWithValue template class.
    template <class TValue>
    class CustomizedPreferenceEntryWithValueSnapshot : public PreferenceEntrySnapshot
    {
    public:
        CustomizedPreferenceEntryWithValueSnapshot(CustomizedPreferenceEntryWithValue<TValue> *);
    protected:
        // Inherits from PreferenceEntrySnapshot...
        virtual bool isChanged() const override;
        virtual void writeBack() override;
    private:
        const TValue m_value;
    };
    
    /// \brief CustomizedPreferenceEntryWithValue template class intends to help
    /// the user to add conveniently a customized preference entry.
    template <class TValue>
    class CustomizedPreferenceEntryWithValue : public CustomizedPreferenceEntry
    {
    public:
        /// \brief Constructor.
        /// \param pGroup: The preference entry group in which this customized preference entry is.
        /// \param key: The key of this preference entry.
        /// \param name: The Xml attribute name.
        /// \param value: The initialized value.
        CustomizedPreferenceEntryWithValue(PreferenceEntryGroup * pGroup,
            const std::wstring & name, const TValue & value);
        virtual ~CustomizedPreferenceEntryWithValue() {}
        /// \return Get the value.
        const TValue & value() const { return m_value; }
        /// \brief Set the new value.
        void setValue(const TValue & val) { m_value = val; }
        /// \brief Check if equal to another value.
        /// \return Return true if equal, otherwise, return false.
        virtual bool isEqualTo(const TValue & another) const { return m_value == another; }
        /// \brief Get the value as std::wstring.
        virtual const std::wstring valueAsString() const = 0;
        /// \brief Set the new value as std::wstring.
        virtual void setValueAsString(const std::wstring & val) = 0;
    protected:
        virtual void set(const CustomizedPreferenceEntry::XmlAttributes &) override;
        virtual void get(CustomizedPreferenceEntry::XmlAttributes &) const override;
        virtual gcmp::OwnerPtr<PreferenceEntrySnapshot> makeSnapshot() override;
    private:
        const std::wstring m_name;
        TValue m_value;
    };
}
    
//_____________________________________________________________________________
// CustomizedPreferenceEntryWithValueSnapshot template class implementation
template <class TValue>
gnuf::CustomizedPreferenceEntryWithValueSnapshot<TValue>::CustomizedPreferenceEntryWithValueSnapshot(
    gnuf::CustomizedPreferenceEntryWithValue<TValue> * pEntry)
    : PreferenceEntrySnapshot(pEntry)
    , m_value(pEntry->value())
{}

template <class TValue>
bool gnuf::CustomizedPreferenceEntryWithValueSnapshot<TValue>::isChanged() const
{
    const gnuf::CustomizedPreferenceEntryWithValue<TValue> * pEntry =
        static_cast< const gnuf::CustomizedPreferenceEntryWithValue<TValue> * >(entry());
    return !(pEntry->isEqualTo(m_value));
}

template <class TValue>
void gnuf::CustomizedPreferenceEntryWithValueSnapshot<TValue>::writeBack()
{
    auto pEntry = entry();
    if (nullptr == pEntry)
        return;

    gnuf::CustomizedPreferenceEntryWithValue<TValue> * pCustomizedEntry =
        static_cast< gnuf::CustomizedPreferenceEntryWithValue<TValue> * >(pEntry);
    pCustomizedEntry->setValue(m_value);
}

//_____________________________________________________________________________
// CustomizedPreferenceEntryWithValue template class implementation
template <class TValue>
gnuf::CustomizedPreferenceEntryWithValue<TValue>::CustomizedPreferenceEntryWithValue(
    PreferenceEntryGroup * pGroup,
    const std::wstring & name, const TValue & value)
    : CustomizedPreferenceEntry(pGroup), m_name(name), m_value(value)
{}

template <class TValue>
void gnuf::CustomizedPreferenceEntryWithValue<TValue>::set(
    const CustomizedPreferenceEntry::XmlAttributes & attributes)
{
    auto found = attributes.find(m_name);
    if (attributes.end() != found) {
        setValueAsString((*found).second);
    }
}

template <class TValue>
void gnuf::CustomizedPreferenceEntryWithValue<TValue>::get(
    CustomizedPreferenceEntry::XmlAttributes & attributes) const
{
    attributes.insert(std::make_pair(m_name, valueAsString()));
}

template <class TValue>
gcmp::OwnerPtr<gnuf::PreferenceEntrySnapshot>
gnuf::CustomizedPreferenceEntryWithValue<TValue>::makeSnapshot()
{
    return TransferOwnership(NEW_AS_OWNER_PTR(
        CustomizedPreferenceEntryWithValueSnapshot<TValue>, this));
}